﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using EzDesk.Apps.Model.Attendance;
using EzDesk.EzAppApi.Utility;
using EzDesk.Utility.Extensions;
using EzDesk.Utility.Json;
using EzDesk.Utility.Log;
using EzDesk.Utility.ServiceStatusInfo;
using EzDesk.Apps.Data.Attendance;

namespace EzDesk.Apps.ServiceImpl.Attendance
{
    public partial class AttendanceService
    {
        #region [ private methods]

        private void addEvaluationChangeItem(Guid historyId, string itemName, string oldValue, string newValue)
        {
            EzDeskAttendanceContainer.GoAndSave(
                container =>
                    {
                        var changeItem = new EvaluationChangeItem()
                        {
                            Id = Guid.NewGuid(),
                            HistoryId = historyId,
                            ItemName = itemName,
                            OldValue = oldValue,
                            NewValue = newValue,
                        };
                        container.EvaluationChangeItemSet.AddObject(changeItem);
                    });

        }

        private void addEvaluationChange(
            Guid historyId,
            DayAttendanceEvaluation oldAttendanceEvaluation,
            DayAttendanceEvaluation newAttendanceEvaluation
            )
        {
            // 记录迟到时间
            if (oldAttendanceEvaluation.LateMiniutes != newAttendanceEvaluation.LateMiniutes)
            {
                addEvaluationChangeItem(
                    historyId,
                    "LateMiniutes",
                    oldAttendanceEvaluation.LateMiniutes.ToString(),
                    newAttendanceEvaluation.LateMiniutes.ToString()
                    );
            }
            // 记录早退时间
            if (oldAttendanceEvaluation.EarlyMiniutes != newAttendanceEvaluation.EarlyMiniutes)
            {
                addEvaluationChangeItem(
                    historyId,
                    "EarlyMiniutes",
                    oldAttendanceEvaluation.EarlyMiniutes.ToString(),
                    newAttendanceEvaluation.EarlyMiniutes.ToString()
                    );
            }
            // 记录是否缺勤
            if (oldAttendanceEvaluation.IsAbsense != newAttendanceEvaluation.IsAbsense)
            {
                addEvaluationChangeItem(
                    historyId,
                    "IsAbsense",
                    oldAttendanceEvaluation.IsAbsense.ToString(),
                    newAttendanceEvaluation.IsAbsense.ToString()
                    );
            }
            // 记是否加班
            if (oldAttendanceEvaluation.IsExtraWork != newAttendanceEvaluation.IsExtraWork)
            {
                addEvaluationChangeItem(
                    historyId,
                    "IsExtraWork",
                    oldAttendanceEvaluation.IsExtraWork.ToString(),
                    newAttendanceEvaluation.IsExtraWork.ToString()
                    );
            }
            // 记录实际工时
            if (oldAttendanceEvaluation.Manhours != newAttendanceEvaluation.Manhours)
            {
                addEvaluationChangeItem(
                    historyId,
                    "ManHours",
                    oldAttendanceEvaluation.Manhours.ToString(),
                    newAttendanceEvaluation.Manhours.ToString()
                    );
            }
        }



        public IList<EvaluationModifyHistory> getModifyHistoryByUserAndDate(Guid userId, DateTime date)
        {
            IList<EvaluationModifyHistory> result = null;
            EzDeskAttendanceContainer.Go(
                container =>
                    {
                        var query = container.EvaluationModifyHistorySet
                            .Where(p => p.UserId == userId && p.Date == date)
                            .OrderByDescending(p => p.ModifyOn)
                            .ToList();

                        foreach (var modifyHistory in query)
                        {
                            container.LoadProperty(modifyHistory, p => p.EvaluationChangeItem);
                            modifyHistory.Operator = _ezApiClient.GetSimpleUserById(modifyHistory.OperatorId);
                        }

                        result = query;
                        
                    });

            return result;
        }

        public void addModifyHistory(
            DayAttendanceEvaluation oldAttendanceEvaluation,
            DayAttendanceEvaluation newAttendanceEvaluation,
            Guid operationId,
            DateTime modifyOn)
        {
            var userId = newAttendanceEvaluation.UserId;
            var newHistoryId = Guid.NewGuid();
            EzDeskAttendanceContainer.GoAndSave(
                container =>
                    {
                        var modifyHistory = new EvaluationModifyHistory()
                                                {
                                                    Id = newHistoryId,
                                                    UserId = userId,
                                                    Date = newAttendanceEvaluation.Date,
                                                    ModifyOn = modifyOn,
                                                    OperatorId = operationId,
                                                };
                        container.EvaluationModifyHistorySet.AddObject(modifyHistory);
                    });
            addEvaluationChange(newHistoryId, oldAttendanceEvaluation, newAttendanceEvaluation);


        }




        private DayAttendanceEvaluation getDayAttendanceEvaluation(DateTime date, Guid userId)
        {
            DayAttendanceEvaluation evaluation = null;
            EzDeskAttendanceContainer.Go(
                container =>
                    {
                        evaluation = container.DayAttendanceEvaluationSet.FirstOrDefault(
                            p => p.Date == date.Date && p.UserId == userId);
                    });
            return evaluation ?? (evaluation = generateDayAttendanceEvaluation(date, userId));
        }

        private DayAttendanceEvaluation generateDayAttendanceEvaluation(DateTime date, Guid userId)
        {
            DayAttendanceEvaluation evaluation = null;
            date = date.Date;
            EzDeskAttendanceContainer.GoAndSave(
                container =>
                    {
                        // 直接生成一个新的对象
                        evaluation = evaluateAttendanceInfo(userId, date);
                        evaluation.Id = Guid.NewGuid();
                        container.DayAttendanceEvaluationSet.AddObject(evaluation);
                        return evaluation;
                    });
            return evaluation;
        }

        #region [ converTo Methods ]
        /// <summary>
        /// 转换完整的EvaluationModifyApplication实体
        /// </summary>
        /// <param name="applicaiton"></param>
        private EvaluationModifyApplication convertEvaluationModifyApplicationModel(EvaluationModifyApplication applicaiton)
        {
            if (applicaiton != null)
            {
                if (applicaiton.ApplyUserId != Guid.Empty)
                {
                    applicaiton.ApplyUser = _ezApiClient.GetSimpleUserById(applicaiton.ApplyUserId);
                }
            }
            return applicaiton;
        }

        /// <summary>
        /// 转换完整的convertEvaluationHistoryList实体
        /// </summary>
        /// <param name="evaluationModifyHistory"></param>
        private IList<EvaluationModifyHistory> convertEvaluationHistoryListModel(IList<EvaluationModifyHistory> evaluationModifyHistory)
        {
            if (evaluationModifyHistory != null)
            {
                foreach (EvaluationModifyHistory modifyHistory in evaluationModifyHistory)
                {
                    if (modifyHistory.OperatorId != Guid.Empty)
                    {
                        modifyHistory.Operator = _ezApiClient.GetSimpleUserById(modifyHistory.OperatorId);
                    }
                }
            }

            return evaluationModifyHistory;
        }
        #endregion

        /// <summary>
        /// 获得某一天能够得到的额外工时
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="date"></param>
        /// <returns></returns>
        private int getExtraManhours(Guid userId, DateTime date)
        {

            return 0;
            // todo: 前一天晚上10点以后下班，则获得1小时额外工时
            throw new NotImplementedException();
        }

        /// <summary>
        /// 重新评估考勤情况并获得评估结果
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="date"></param>
        /// <returns></returns>
        private DayAttendanceEvaluation evaluateAttendanceInfo(Guid userId, DateTime date)
        {
            var config = getAttendanceConfig();
            var attendanceInfoList = GetAttendanceInfoListByUserInDay(date, userId).Result;
            var dayInfo = GetDayInfo(userId, date).Result;
            var dayLeaveInfo = GetDayLeaveInfo(userId, date).Result;
            int extraManhours = getExtraManhours(userId, date);

            var evaluator = new AttendanceEvaluator(
                userId,
                config,
                attendanceInfoList,
                dayInfo,
                dayLeaveInfo,
                extraManhours
                );
            return evaluator.GetAttendanceEvaluation();
        }

        


        /// <summary>
        /// 添加一个新的修改考勤情况评估申请
        /// </summary>
        /// <param name="modifyApplicaiton"></param>
        private void addNewModifyAttendanceEvaluationApplication(EvaluationModifyApplication modifyApplicaiton)
        {
            EzDeskAttendanceContainer.GoAndSave(
                container =>
                    {
                        modifyApplicaiton.Id = Guid.NewGuid();
                        container.EvaluationModifyApplicationSet.AddObject(modifyApplicaiton);
                    });
        }

        /// <summary>
        /// 更新自定义考勤评估状况
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="newAttendanceEvaluation"></param>
        private void updateDayAttendanceEvaluation(DayAttendanceEvaluation newAttendanceEvaluation)
        {
            EzDeskAttendanceContainer.GoAndSave(
                container =>
                {
                    container.DayAttendanceEvaluationSet.Attach(newAttendanceEvaluation);
                    container.ObjectStateManager.ChangeObjectState(newAttendanceEvaluation,
                                                                   EntityState.Modified);
                });
        }

        /// <summary>
        /// 更新考勤评估状况修改申请状态
        /// </summary>
        /// <param name="attendanceEvaluationApplication"></param>
        private void updateEvaluationModifyApplication(EvaluationModifyApplication attendanceEvaluationApplication)
        {
            EzDeskAttendanceContainer.GoAndSave(
                container =>
                    {
                        container.EvaluationModifyApplicationSet.Attach(attendanceEvaluationApplication);
                        container.ObjectStateManager.ChangeObjectState(attendanceEvaluationApplication,
                                                                       EntityState.Modified);
                    });
        }


        /// <summary>
        /// 直接获得当前正在惊醒中的考勤状况修改评估申请
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="date"></param>
        /// <returns></returns>
        private EvaluationModifyApplication getCurrentAttendanceEvaluationModifyApplication(Guid userId, DateTime date)
        {
            var latestApplication = EzDeskAttendanceContainer.Go(
                container =>
                    {
                        date = date.Date;
                        var applicationList =
                            container.EvaluationModifyApplicationSet.Where(
                                p => p.ApplyUserId == userId && p.Date == date);
                        EvaluationModifyApplication application = null;

                        // 按时间顺序找最新的一个
                        if (applicationList.FirstOrDefault() != null)
                        {
                            application = applicationList
                                .Where(p => p.StateValue == (int) ApplyState.Applying)
                                .OrderByDescending(p => p.ApplyOn)
                                .FirstOrDefault();
                        }
                        application = convertEvaluationModifyApplicationModel(application);
                        return application;
                    });
            return latestApplication;
        }

        /// <summary>
        /// 获得某人所有的考勤评估申请
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="date"></param>
        /// <returns></returns>
        private IList<EvaluationModifyApplication> converToEvaluationModifyApplicationModel(Guid userId, DateTime date)
        {
            var applicationList = EzDeskAttendanceContainer.Go(
                container =>
                    {
                        date = date.Date;
                        return
                            container.EvaluationModifyApplicationSet.Where(
                                p => p.ApplyUserId == userId && p.Date == date)
                                .Select(convertEvaluationModifyApplicationModel)
                                .ToList();

                    });
            return applicationList;
        }


        /// <summary>
        /// 为考勤添加一个考勤申请拒绝历史
        /// </summary>
        /// <param name="theApplication"></param>
        /// <param name="actorId"></param>
        private void addAttendanceEvaluationRejectionToHistroy(EvaluationModifyApplication theApplication, Guid actorId)
        {
            // 这里的逻辑是：添加一个修改历史，但是不添加任何ChangeItem，就认为是被拒绝了
            EzDeskAttendanceContainer.GoAndSave(
                container => container.EvaluationModifyHistorySet.AddObject(
                    new EvaluationModifyHistory()
                        {
                            Id = Guid.NewGuid(),
                            UserId = theApplication.ApplyUserId,
                            Date = theApplication.Date,
                            OperatorId = actorId,
                            ModifyOn = DateTime.Now
                        }));

        }

        /// <summary>
        /// 取得考勤评估修改的历史
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="date"></param>
        /// <returns></returns>
        private IList<EvaluationModifyHistory> getAttenanceEvaluationModifyHistory(Guid userId, DateTime date)
        {
            date = date.Date;
            return convertEvaluationHistoryListModel(
                getModifyHistoryByUserAndDate(userId, date)
                );
        }



        /// <summary>
        /// 申请修改考勤评估后，发送Feed
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="date"></param>
        /// <param name="applyText"></param>
        private void sendApplyEvaluationModifyFeed(EvaluationModifyApplication modifyApplication, string applyText)
        {
            var userId = modifyApplication.ApplyUserId;
            var date = modifyApplication.Date;
            var senderUser = _ezApiClient.GetSimpleUserById(userId);

            string dateStr = date.ToFullDate();
            string dateEpoch = date.ToEpoch().ToString();

            var feedData = new Dictionary<string, string>
                               {
                                   {"sender", senderUser.Name},
                                   {"dateStr", dateStr},
                                   {"dateEpoch", dateEpoch},
                                   {"targetUserId", userId.ToString()},
                                   {"applyText", applyText},
                               };

            // 发送目标为所有hr
            var memberString = _ezApiClient.GetGroupByName("attendance_hr").Memberstring;

            var feedId = _ezApiClient.SendFeed(
                FeedTemplateStr_attendance_applyEvaluationModify,
                JsonHelper.ConvertToJson(feedData),
                memberString
                );
            _ezApiClient.SetFeedRecord(modifyApplication.Id, FeedTemplateStr_attendance_applyEvaluationModify,
                                       userId.ToString(), feedId, null);
        }

        /// <summary>
        /// 发送考勤评估信息已经修改的Feed
        /// </summary>
        /// <param name="operatorId"></param>
        /// <param name="newAttendanceEvaluation"></param>
        private void sendEvalationModifiedFeed(Guid operatorId, DayAttendanceEvaluation oldAttendanceEvaluation, DayAttendanceEvaluation newAttendanceEvaluation)
        {
            var operatorUser = _ezApiClient.GetSimpleUserById(operatorId);
            DateTime today = newAttendanceEvaluation.Date;
            string dateStr = today.ToFullDate();
            string dateEpoch = today.ToEpoch().ToString();

            var isFirst = true;
            var modifyHistroyBuild = new StringBuilder();
            if (newAttendanceEvaluation.IsAbsense != oldAttendanceEvaluation.IsAbsense)
            {
                var isAbsenseOld = "是";
                var isAbsenseNew = "是";
                if (oldAttendanceEvaluation.IsAbsense == false)
                {
                    isAbsenseOld = "否";
                }

                if (newAttendanceEvaluation.IsAbsense == false)
                {
                    isAbsenseNew = "否";
                }
                modifyHistroyBuild.AppendFormat("将旷工 {0} 改为 {1}", isAbsenseOld, isAbsenseNew);
                isFirst = false;
            }
            if (newAttendanceEvaluation.IsExtraWork != oldAttendanceEvaluation.IsExtraWork)
            {
                var isExtraWorkNew = "是";
                var isExtraWorkOld = "是";
                if (oldAttendanceEvaluation.IsExtraWork == false)
                {
                    isExtraWorkOld = "否";
                }
                if (newAttendanceEvaluation.IsExtraWork == false)
                {
                    isExtraWorkNew = "否";
                }
                if (!isFirst)
                {
                    modifyHistroyBuild.Append("<ez:br />");
                }
                modifyHistroyBuild.AppendFormat("将加班 {0}改为 {1}", isExtraWorkOld, isExtraWorkNew);
                isFirst = false;
            }
            if (newAttendanceEvaluation.LateMiniutes != oldAttendanceEvaluation.LateMiniutes)
            {
                if (!isFirst)
                {
                    modifyHistroyBuild.Append("<ez:br />");
                }
                modifyHistroyBuild.AppendFormat("将迟到 {0} 分钟 改为 {1} 分钟", oldAttendanceEvaluation.LateMiniutes, newAttendanceEvaluation.LateMiniutes);
                isFirst = false;
            }
            if (newAttendanceEvaluation.EarlyMiniutes != oldAttendanceEvaluation.EarlyMiniutes)
            {
                if (!isFirst)
                {
                    modifyHistroyBuild.Append("<ez:br />");
                }
                modifyHistroyBuild.AppendFormat("将早退 {0} 分钟 改为 {1} 分钟", oldAttendanceEvaluation.EarlyMiniutes, newAttendanceEvaluation.EarlyMiniutes);
                isFirst = false;
            }
            if (newAttendanceEvaluation.Manhours != oldAttendanceEvaluation.Manhours)
            {
                if (!isFirst)
                {
                    modifyHistroyBuild.Append("<ez:br />");
                }
                modifyHistroyBuild.AppendFormat("将获得工时 {0} 小时 改为 {1} 小时", oldAttendanceEvaluation.Manhours.DivEx(10), newAttendanceEvaluation.Manhours.DivEx(10));
                isFirst = false;
            }

            var feedData = new Dictionary<string, string>
                               {
                                   {"actor", operatorUser.Name},
                                   {"dateStr", dateStr},
                                   {"dateEpoch", dateEpoch},
                                   {"modifyHistory", modifyHistroyBuild.ToString()}
                               };

            // 发送给被修改考勤评估的人
            string memberString = newAttendanceEvaluation.UserId.ToString();

            _ezApiClient.SendFeed(
                FeedTemplateStr_attendance_evaluationModified,
                JsonHelper.ConvertToJson(feedData),
                memberString
                );

        }


        /// <summary>
        /// 发送申请被拒绝的Feed
        /// </summary>
        private void sendEvalationRejectedFeed(Guid actorId, EvaluationModifyApplication theApplication)
        {
            var actor = _ezApiClient.GetSimpleUserById(actorId);
            var feedData = new Dictionary<string, string>
                               {
                                   {"actor", actor.Name},
                                   {"dateStr", theApplication.Date.ToFullDate()},
                                   {"timeEpoch", theApplication.Date.ToEpoch().ToString()},
                               };
            _ezApiClient.SendFeed(
                FeedTemplateStr_attendance_evaluationRejected,
                JsonHelper.ConvertToJson(feedData),
                theApplication.ApplyUserId.ToString()
                );
        }

        /// <summary>
        /// 根据申请人，申请时间，获得申请修改考勤评估的Feed
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="date"></param>
        private void removeEvalationModifiedFeed(Guid feedId)
        {
            if (feedId != Guid.Empty)
            {
                // 获取，让所有HR的收到的提醒都失效
                var memberString = _ezApiClient.GetGroupByName("attendance_hr").Memberstring;
                _ezApiClient.SetFeedIsAvailable(feedId, memberString, false);
            }
        }



        #endregion




        public StatusInfo<DayAttendanceEvaluation> GetDayAttendanceEvaluation(Guid userId, DateTime date)
        {
            return StatusInfo.Wrap(
                () => getDayAttendanceEvaluation(date, userId));
        }

        public StatusInfo<List<DayAttendanceEvaluation>> GetDayAttendanceEvaluations(Guid userId, DateTime startDate, DateTime endDate)
        {
            return StatusInfo.Wrap(
                () => getDataBetweenDays(startDate, endDate, day => getDayAttendanceEvaluation(day, userId)));
        }


        public StatusInfo ApplyEvaluationModify(Guid userId, DateTime date, string applyText)
        {
            return StatusInfo.Wrap(
                () =>
                {
                    date = date.Date;

                    if (DateTime.Now.Date <= date)
                    {
                        LogEx.EXCEPT("Attendance - ApplyEvaluationModify", "还没到这一天，没法处理这一天的考勤评估修改");
                    }

                    // 如果已经有提交的正在处理中的申请，那么不允许多次提交
                    if (getCurrentAttendanceEvaluationModifyApplication(userId, date) != null)
                    {
                        LogEx.EXCEPT("Attendance - ApplyEvaluationModify", "重复提交修改考勤评估申请");
                    }

                    var modifyApplicaiton = new EvaluationModifyApplication()
                                                {
                                                    ApplyOn = DateTime.Now,
                                                    ApplyUserId = userId,
                                                    ApplyText = applyText,
                                                    Date = date.Date,
                                                    State = ApplyState.Applying,
                                                };

                    addNewModifyAttendanceEvaluationApplication(modifyApplicaiton);

                    sendApplyEvaluationModifyFeed(modifyApplicaiton, applyText);
                });
        }

        public StatusInfo<EvaluationModifyApplication> GetCurrentEvaluationModifyApplication(Guid userId, DateTime date)
        {
            return StatusInfo.Wrap(
                () => getCurrentAttendanceEvaluationModifyApplication(userId, date));
        }
        public StatusInfo<List<EvaluationModifyApplication>> GetCurrentEvaluationModifyApplications(Guid userId, DateTime startDate, DateTime endDate)
        {
            return
                StatusInfo.Wrap(
                    () =>
                    getDataBetweenDays(startDate, endDate,
                                       day => getCurrentAttendanceEvaluationModifyApplication(userId, day)));
        }

        public StatusInfo<IList<EvaluationModifyApplication>> GetEvaluationModifyApplications(Guid userId, DateTime date)
        {
            return StatusInfo.Wrap(
                () => converToEvaluationModifyApplicationModel(userId, date));
        }

        public StatusInfo<IList<EvaluationModifyHistory>> GetEvaluationModifyHistory(Guid userId, DateTime date)
        {
            return StatusInfo.Wrap(
                () => getAttenanceEvaluationModifyHistory(userId, date));
        }

        public StatusInfo ModifyEvaluation(DayAttendanceEvaluation newAttendanceEvaluation, Guid operatorId)
        {
            return StatusInfo.Wrap(
                () =>
                {
                    var userId = newAttendanceEvaluation.UserId;
                    var date = newAttendanceEvaluation.Date = newAttendanceEvaluation.Date.Date;

                    // 为考勤添加一个考勤历史
                    var oldAttendanceEvaluation = GetDayAttendanceEvaluation(userId, date).Result;

                    addModifyHistory(oldAttendanceEvaluation, newAttendanceEvaluation, operatorId, DateTime.Now);
                    
                    // 更新考勤评估记录
                    updateDayAttendanceEvaluation(newAttendanceEvaluation);

                    // 将当前的考勤申请变为已处理
                    var currentEvaluationApplication = getCurrentAttendanceEvaluationModifyApplication(userId, date);
                    if (currentEvaluationApplication != null)
                    {
                        currentEvaluationApplication.State = ApplyState.Done;
                        updateEvaluationModifyApplication(currentEvaluationApplication);

                        // 删除申请修改考勤评估记录的Feed
                        var feedId = _ezApiClient.GetFeedRecord(currentEvaluationApplication.Id,
                                                                FeedTemplateStr_attendance_applyEvaluationModify,
                                                                userId.ToString());
                        removeEvalationModifiedFeed(Guid.Parse(feedId));
                    }
                    // 发送考勤评估记录已修改的Feed
                    sendEvalationModifiedFeed(operatorId, oldAttendanceEvaluation, newAttendanceEvaluation);
                });
        }

        public StatusInfo RejectEvaluationApplication(Guid actorId, Guid applicationId)
        {
            return StatusInfo.Wrap(
                () => EzDeskAttendanceContainer.GoAndSave(
                    container =>
                        {
                            var theApplication =
                                container.EvaluationModifyApplicationSet.FirstOrDefault(
                                    p => p.Id == applicationId);

                            // 为考勤添加一个考勤申请拒绝历史
                            addAttendanceEvaluationRejectionToHistroy(theApplication, actorId);

                            if (theApplication.State == ApplyState.Applying)
                            {
                                // 确保这个申请当前有效
                                theApplication.State = ApplyState.Done;
                                updateEvaluationModifyApplication(theApplication);

                                // 删除申请修改考勤评估记录的Feed
                                var feedId = _ezApiClient.GetFeedRecord(theApplication.Id,
                                                                        FeedTemplateStr_attendance_applyEvaluationModify,
                                                                        theApplication.ApplyUserId.ToString());
                                removeEvalationModifiedFeed(Guid.Parse(feedId));
                            }
                            // 发送申请被拒绝的Feed


                            // todo:发送申请被拒绝的Feed
                            sendEvalationRejectedFeed(actorId, theApplication);
                        }));
        }

    }
}
